/**
 * This file is part of Megasquirt Logger for PalmOS.
 * Copyright (C) 2011 Andrew Beard
 *
 * Megasquirt Logger is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Megasquirt Logger is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Megasquirt Logger.  If not, see <http://www.gnu.org/licenses/>.
 */
#include "MegasquirtInterface.h"
#include <SerialMgr.h>
#include <MemoryMgr.h>

MegasquirtInterface::MegasquirtInterface( UInt16 nPortID,
                                          UInt16 nRTU_Length ) : m_LastError( errNone ),
                                                                 m_eInterfaceState( CONNECTED ),
                                                                 m_nSerialPortID( nPortID ),
                                                                 m_nRealtimeUpdateLength( nRTU_Length ),
                                                                 m_bSignatureReceived( false ),
                                                                 m_bRealtimeUpdateEvent( false ),
                                                                 m_bSignatureEvent( false ),
                                                                 m_bDataReady( false )
{
    m_pRealtimeUpdateData = new UInt8[ m_nRealtimeUpdateLength ];
    m_Signature[0]                = '\0';
	m_Signature[SIGNATURE_LENGTH] = '\0';
    
    m_LastError = SrmSetWakeupHandler( m_nSerialPortID,
                                       RequestedDataReadyCallback,
                                       (UInt32)&m_bDataReady );
}

MegasquirtInterface::~MegasquirtInterface()
{
    if ( m_pRealtimeUpdateData )
    {
        delete[] m_pRealtimeUpdateData;
        m_pRealtimeUpdateData = NULL;
    }
    
    m_LastError = SrmSetWakeupHandler( m_nSerialPortID,
                                       NULL,
                                       0 );
    SrmSetReceiveBuffer( m_nSerialPortID,
                         NULL,
                         0 );
}

void MegasquirtInterface::ProcessRxData( void )
{
    UInt32 nBytesWaiting  = 0;
	UInt8* pRxDataFifo    = NULL;
    
    if ( m_bDataReady )
    {
        switch ( m_eInterfaceState )
        {
            case CONNECTED:
            {
                // We don't need to worry about any incomming data
                break;
            }
            case WAITING_FOR_SIGNATURE:
            {
			    m_LastError = SrmReceiveWindowOpen( m_nSerialPortID,
				                                    &pRxDataFifo,
													&nBytesWaiting );
				if ( m_LastError != errNone )
				{
				    // Bad things happened
					return;
				}
				
				if ( nBytesWaiting >= SIGNATURE_LENGTH )
				{
				    // Copy data out of the FIFO
				    for ( UInt16 i = 0; i < SIGNATURE_LENGTH; i++ )
				    {
				        m_Signature[i] = pRxDataFifo[i];
				    }
					m_bSignatureEvent    = true;
					m_bSignatureReceived = true;
			        m_eInterfaceState    = CONNECTED;
			
					// Flush all the data in the FIFO
					m_LastError = SrmReceiveWindowClose( m_nSerialPortID,
				                                         nBytesWaiting );
				}
				else
				{
				    // Not enough data, this shouldn't happen
                    m_LastError = SrmReceiveWindowClose( m_nSerialPortID,
				                                         0 );
				}

                break;
            }
            case WAITING_FOR_REALTIME_UPDATE:
            {
			    m_LastError = SrmReceiveWindowOpen( m_nSerialPortID,
				                                    &pRxDataFifo,
													&nBytesWaiting );
				if ( m_LastError != errNone )
				{
				    // Bad things happened
					return;
				}
				
				if ( nBytesWaiting >= m_nRealtimeUpdateLength )
				{
				    // Copy data out of the FIFO
					for ( UInt16 i = 0; i < m_nRealtimeUpdateLength; i++ )
				    {
				        m_pRealtimeUpdateData[i] = pRxDataFifo[i];
				    }
					m_bRealtimeUpdateEvent = true;
			        m_eInterfaceState      = CONNECTED;
					
					// Flush all the data in the FIFO
					m_LastError = SrmReceiveWindowClose( m_nSerialPortID,
				                                         nBytesWaiting );
				}
				else
				{
				    // Not enough data, this shouldn't happen
                    m_LastError = SrmReceiveWindowClose( m_nSerialPortID,
				                                         0 );
				}
                break;
            }
            default:
            {
               // How did we get here?
               m_eInterfaceState = CONNECTED;
               break;
            }
        }
        m_bDataReady = false;
    }
}

void MegasquirtInterface::RequestSignature( void )
{
    if ( m_eInterfaceState == CONNECTED )
    {
        UInt32 nBytesSent = 0;
    
        m_eInterfaceState = WAITING_FOR_SIGNATURE;
        
        m_LastError = SrmReceiveFlush( m_nSerialPortID,
                                       0 );
                                       
        m_LastError = SrmPrimeWakeupHandler( m_nSerialPortID,
                                             SIGNATURE_LENGTH );
        
        nBytesSent = SrmSend( m_nSerialPortID,
                              &SIGNATURE_COMMAND,
                              sizeof(SIGNATURE_COMMAND),
                              &m_LastError );
    }
    else
    {
        // FIXME: Throw an error or something
    }
}

void MegasquirtInterface::RequestRealtimeUpdate( void )
{
    if ( m_eInterfaceState == CONNECTED )
    {
        UInt32 nBytesSent = 0;
    
        m_eInterfaceState = WAITING_FOR_REALTIME_UPDATE;
        
        m_LastError = SrmReceiveFlush( m_nSerialPortID,
                                       0 );
        m_LastError = SrmSetReceiveBuffer( m_nSerialPortID,
                                           m_RxBuffer,
                                           RX_BUFFER_SIZE );
        m_LastError = SrmPrimeWakeupHandler( m_nSerialPortID,
                                             m_nRealtimeUpdateLength );
        
        nBytesSent = SrmSend( m_nSerialPortID,
                              &REALTIME_UPDATE_COMMAND,
                              sizeof(REALTIME_UPDATE_COMMAND),
                              &m_LastError );
    }
    else
    {
        // FIXME: Throw an error or something
    }
}

bool MegasquirtInterface::ReceivedSignature( void ) const
{
    return m_bSignatureReceived;
}

const Char* MegasquirtInterface::GetSignature( void ) const
{
    return m_Signature;
}

const UInt8* MegasquirtInterface::GetRealtimeUpdateData( void ) const
{
    return m_pRealtimeUpdateData;
}

bool MegasquirtInterface::CheckSignatureEvent( void )
{
    if ( m_bSignatureEvent )
    {
        m_bSignatureEvent = false;
        return true;
    }
    else
    {
        return false;
    }
}

bool MegasquirtInterface::CheckRealtimeUpdateEvent( void )
{
    if ( m_bRealtimeUpdateEvent )
    {
        m_bRealtimeUpdateEvent = false;
        return true;
    }
    else
    {
        return false;
    }
}

void MegasquirtInterface::RequestedDataReadyCallback( UInt32 refCon )
{
    bool* pDataReady = (bool*)refCon;
    
    *pDataReady = true;
    EvtWakeup();
}